package com.ihr360.authority.sharing.service.impl;

import com.ihr360.authority.sharing.dao.AuthUserResourceRepository;
import com.ihr360.authority.sharing.model.dto.AuthUserResource;
import com.ihr360.authority.sharing.model.vo.SimpleAuthUserResourceVo;
import com.ihr360.authority.sharing.service.AuthUserResourceService;
import com.ihr360.authority.sharing.validator.SessionCompanyInfoValidator;
import com.ihr360.authority.sharing.validator.SessionUserInfoValidator;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.data.util.Pair;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.type.AccessPrivilege;
import com.ihr360.commons.model.type.ResourcePublicity;
import com.ihr360.validation.annonation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class AuthUserResourceServiceImpl implements AuthUserResourceService {

    @Autowired
    private AuthUserResourceRepository authUserResourceRepository;

    @Override
    public Pair<Map<Object, Integer>, Map<Object, Integer>> queryUserResourcesPrivilege(Class bizClass, Class keyClass) {
        List<SimpleAuthUserResourceVo> simpleAuthUserResourceVos = queryUserResources(bizClass);
        if (CollectionUtils.isEmpty(simpleAuthUserResourceVos)) {
            return null;
        }

        String currentUserId = Ihr360SessionContextHolder.getSession().getUserInfo().getUserId();

        Map<Object, Integer> idsPrivilegeMap = new HashMap<>();
        Map<Object, Integer> idsPublicityMap = new HashMap<>();
        simpleAuthUserResourceVos.forEach(authUserResourceVo -> {
            String sourceId = authUserResourceVo.getResourceId();
            if(Objects.equals(currentUserId, authUserResourceVo.getUserId())) {
                idsPrivilegeMap.put(sourceId, AccessPrivilege.getPrivilege(authUserResourceVo.getPrivilege()));
            } else {
                idsPrivilegeMap.put(sourceId, AccessPrivilege.Privilege.READONLY);
            }
            idsPublicityMap.put(sourceId, ResourcePublicity.getPublicity(authUserResourceVo.getPublicity()));
        });
        if (keyClass.equals(String.class)) {
            return Pair.of(idsPrivilegeMap, idsPublicityMap);
        }
        if (keyClass.equals(Long.class)) {
            Map<Object, Integer> newMap = new HashMap<>(idsPrivilegeMap.size());
            idsPrivilegeMap.forEach((key, value) -> newMap.put(Long.valueOf(key.toString()), value));

            Map<Object, Integer> newMap1 = new HashMap<>(idsPrivilegeMap.size());
            idsPublicityMap.forEach((key, value) -> newMap1.put(Long.valueOf(key.toString()), value));
            return Pair.of(newMap, newMap1);
        }
        return Pair.of(idsPrivilegeMap, idsPublicityMap);
    }


    @Override
    public Map<Long, Integer> queryUserResourcesPrivilegeWithLongId(Class bizClass) {
        List<SimpleAuthUserResourceVo> simpleAuthUserResourceVos = queryUserResources(bizClass);
        if (CollectionUtils.isEmpty(simpleAuthUserResourceVos)) {
            return null;
        }
        Map<Long, Integer> idsPrivilegeMap = simpleAuthUserResourceVos.stream().collect(Collectors.toMap(s -> Long.valueOf(s.getResourceId()), s -> AccessPrivilege.getPrivilege(s.getPrivilege())));
        return idsPrivilegeMap;
    }

    @Override
    public Pair<Integer, Integer> queryUserResourcePrivilege(@Valid(validator = SessionUserInfoValidator.class)  Class bizClass, String resourceId) {
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();
        List<AuthUserResource> authUserResource = authUserResourceRepository.findResourcePrivilege(authUserInfo.getCompanyId(), bizClass.getSimpleName(), authUserInfo.getUserId(), resourceId);
        if (CollectionUtils.isEmpty(authUserResource)) {
            return null;
        }
         String currentUserId = authUserInfo.getUserId();

        for (AuthUserResource au : authUserResource) {
            if (au.getUserId().equals(currentUserId)) {
                return Pair.of(au.getPrivilege(), au.getPublicity());
            } else if (au.getPublicity() == 8) {
                return Pair.of(AccessPrivilege.Privilege.READONLY, ResourcePublicity.Publicity.NONE);
            }
        }
        return Pair.of(AccessPrivilege.Privilege.NONE, ResourcePublicity.Publicity.NONE);
    }

    @Override
    //@EnableValidation
    public List<SimpleAuthUserResourceVo> queryUserResources(@Valid(validator = SessionUserInfoValidator.class) Class bizClass) {
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();
        return this.queryUserResources(authUserInfo.getCompanyId(), bizClass, authUserInfo.getUserId());
    }

    @Override
    public Page<AuthUserResource> getUserResources(@Valid(validator = SessionUserInfoValidator.class) Class bizClass, Pageable pageable) {
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();
        return this.getUserResources(authUserInfo.getCompanyId(), bizClass, authUserInfo.getUserId(), pageable);
    }

    @Override
    public Page<SimpleAuthUserResourceVo> getResourcesUsers(@Valid(validator = SessionCompanyInfoValidator.class) String bizClass, String resourceId, Pageable pageable) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        return this.getResourcesUsers(companyId, bizClass, resourceId, pageable);
    }

    @Override
    public List<SimpleAuthUserResourceVo> getResourceUsers(String bizModel, String resourceId) {
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();
        List<AuthUserResource> all = authUserResourceRepository.findAllByCompanyIdAndAndBizModelAndResourceId(
                authUserInfo.getCompanyId(), bizModel, resourceId);
        return convertToVos(all);
    }

    @Override
    public List<SimpleAuthUserResourceVo> getResourceUsers(String companyId, String bizModel, String resourceId) {
        List<AuthUserResource> all = authUserResourceRepository.findAllByCompanyIdAndAndBizModelAndResourceId(
                companyId, bizModel, resourceId);
        return convertToVos(all);
    }

    @Override
    public int queryUserResourceCount(@Valid(validator = SessionUserInfoValidator.class) Class bizClass, String resource) {
        AuthUserInfo authUserInfo = Ihr360SessionContextHolder.getSession().getUserInfo();
        return this.queryUserResourceCount(authUserInfo.getCompanyId(), bizClass, authUserInfo.getUserId(), resource);
    }

    @Override
    public List<SimpleAuthUserResourceVo> queryUserResources(String companyId, Class clazz, String userId) {
        List<AuthUserResource> authUserResources = authUserResourceRepository.queryUserResources(companyId, clazz.getSimpleName(), userId);
        return convertToVos(authUserResources);
    }

    @Override
    public Page<AuthUserResource> getUserResources(String companyId, Class bizModel, String userId, Pageable pageable) {
        return authUserResourceRepository.findAccessableBizResources(companyId, bizModel.getSimpleName(), userId, pageable);
    }

    @Override
    public Page<SimpleAuthUserResourceVo> getResourcesUsers(String companyId, String bizModel, String resourceId, Pageable pageable) {
        Page<AuthUserResource> userResources = authUserResourceRepository.findByCompanyIdAndBizModelAndResourceId(companyId, bizModel, resourceId, pageable);
        return new PageImpl<>(convertToVos(userResources.getContent()), pageable, userResources.getTotalElements());
    }

    @Override
    public int queryUserResourceCount(String companyId, Class bizModel, String userId, String resource) {
        return authUserResourceRepository.queryUserResourceCount(companyId, bizModel.getSimpleName(), userId, resource);
    }

    @Override
    public boolean checkOwner(String companyId, String bizModel, String userId, String resourceId) {
        AuthUserResource authUserResource = authUserResourceRepository.takeOne(companyId, bizModel, userId, resourceId);
        return authUserResource != null && authUserResource.getPrivilege() == AccessPrivilege.Privilege.OWNER;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(String resourceId, List<SimpleAuthUserResourceVo> updatedVos, List<Long> deletedIds) {
        if (CollectionUtils.isNotEmpty(updatedVos)) {
            List<AuthUserResource> authUserResources = convertToDtos(updatedVos);
            authUserResourceRepository.save(authUserResources);
        }
        if (CollectionUtils.isNotEmpty(deletedIds)) {
            Iterable<AuthUserResource> all = authUserResourceRepository.findAll(deletedIds);
            authUserResourceRepository.delete(all);
        }
    }

    @Override
    public AuthUserResource takeOne(String companyId, String bizModel, String userId, String resourceId) {
        return authUserResourceRepository.takeOne(companyId, bizModel, userId, resourceId);
    }

    private List<SimpleAuthUserResourceVo> convertToVos(List<AuthUserResource> dtos) {
        if (CollectionUtils.isEmpty(dtos)) {
            return Collections.emptyList();
        }
        List<SimpleAuthUserResourceVo> vos = new ArrayList<>(dtos.size());
        dtos.forEach(dto -> {
            SimpleAuthUserResourceVo vo = new SimpleAuthUserResourceVo();
            vo.setId(dto.getId());
            vo.setResourceId(dto.getResourceId());
            vo.setUserId(dto.getUserId());
            vo.setUserAccount(dto.getUserAccount());
            vo.setPrivilege(AccessPrivilege.getAccessPrivilege(dto.getPrivilege()));
            vo.setPublicity(ResourcePublicity.getResourcePublicity(dto.getPublicity()));
            vos.add(vo);
        });
        return vos;
    }

    private List<AuthUserResource> convertToDtos(List<SimpleAuthUserResourceVo> vos) {
        if (CollectionUtils.isEmpty(vos)) {
            return Collections.emptyList();
        }
        List<AuthUserResource> dtos = new ArrayList<>(vos.size());
        vos.forEach(vo -> {
            AuthUserResource dto = new AuthUserResource();
            dto.setId(vo.getId());
            dto.setBizModel(vo.getBizModel());
            dto.setResourceId(vo.getResourceId());
            dto.setUserId(vo.getUserId());
            dto.setUserAccount(vo.getUserAccount());
            dto.setPrivilege(AccessPrivilege.getPrivilege(vo.getPrivilege()));
            dto.setPublicity(ResourcePublicity.getPublicity(vo.getPublicity()));
            dtos.add(dto);
        });
        return dtos;
    }

    @Override
    public void addUserResource(String companyId, String bizModel, List<String> userIdList, String resourceId) {
        List<AuthUserResource> authUserResourceList = authUserResourceRepository.findAllByCompanyIdAndBizModelAndResourceIdAndUserIdIn(companyId, bizModel, resourceId, userIdList);
        List<String> existUser = authUserResourceList.stream().map(AuthUserResource::getUserId).collect(Collectors.toList());
        List<AuthUserResource> list = new ArrayList<>();
        userIdList.forEach(userId -> {
            if (!existUser.contains(userId)) {
                list.add(structureResource(companyId, bizModel, userId, "", resourceId, AccessPrivilege.READONLY,
                        ResourcePublicity.PERSONAL));
            }
        });
        if (CollectionUtils.isNotEmpty(list)) {
            authUserResourceRepository.save(list);
        }
    }

    @Override
    public List<AuthUserResource> copyResourceAuthToNewResource(String resourceSourceId, String resourceSourceClassSimpleName,
                                                                List<String> resourceTargetIdList, String resourceTargetClassSimpleName) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        List<AuthUserResource> authUserResourceList = new ArrayList<>();
        if (CollectionUtils.isEmpty(resourceTargetIdList)) {
            return authUserResourceList;
        }

        List<SimpleAuthUserResourceVo> authUserList = getResourceUsers(resourceSourceClassSimpleName, resourceSourceId);
        List<String> userIdList = authUserList.stream().map(SimpleAuthUserResourceVo::getUserId).collect(Collectors.toList());
        resourceTargetIdList.forEach(resourceTargetId -> {
            List<AuthUserResource> exitAuthUserResourceList =
                    authUserResourceRepository.findAllByCompanyIdAndBizModelAndResourceIdAndUserIdIn(companyId, resourceTargetClassSimpleName, resourceTargetId, userIdList);
            List<String> existUser = exitAuthUserResourceList.stream().map(AuthUserResource::getUserId).collect(Collectors.toList());
            authUserList.forEach(resourceVo -> {
                if (!existUser.contains(resourceVo.getUserId())) {
                    authUserResourceList.add(structureResource(companyId, resourceTargetClassSimpleName, resourceVo.getUserId(),
                            resourceVo.getUserAccount(), resourceTargetId, resourceVo.getPrivilege(), resourceVo.getPublicity()));
                }
            });
        });

        if (CollectionUtils.isNotEmpty(authUserResourceList)) {
            authUserResourceRepository.save(authUserResourceList);
        }
        return authUserResourceList;
    }

    private AuthUserResource structureResource(String companyId, String bizModel, String userId, String userAccount, String resourceId,
                                               AccessPrivilege accessPrivilege, ResourcePublicity resourcePublicity) {
        AuthUserResource authUserResource = new AuthUserResource();
        authUserResource.setBizModel(bizModel);
        authUserResource.setPrivilege(AccessPrivilege.getPrivilege(accessPrivilege));
        authUserResource.setPublicity(ResourcePublicity.getPublicity(resourcePublicity));
        authUserResource.setResourceId(resourceId);
        authUserResource.setUserAccount(userAccount);
        authUserResource.setUserId(userId);
        authUserResource.setCompanyId(companyId);
        authUserResource.setCreatedAt(new Date());
        return authUserResource;
    }
}